package com.jumaojiang.utils;


import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * jwt工具类
 * @author wuhanwei
 * @version 1.0
 * @date 2021/10/28
 */
public class JWTUtil {

    public static void main(String[] args) {
        Map<String, Object> payLoadMap = new HashMap<>();
        payLoadMap.put(PAYLOAD_USER_ID, null);
        payLoadMap.put(PAYLOAD_USER_WX_HEAD_IMG, null);
        payLoadMap.put(PAYLOAD_USER_WX_NICK_NAME, null);
        payLoadMap.put(PAYLOAD_USER_IP_ADDRESS, null);
        payLoadMap.put(WHETHER_OPEN_ADDRESS_VERIFICATION, null);
        String jwtToken = JWTUtil.generToken(payLoadMap);
        System.out.println(jwtToken);
        String jwtToken2 = JWTUtil.updateToken(jwtToken);
        System.out.println(jwtToken2);

        byte[] decode = Base64.getDecoder().decode("eyJoZWFkSW1nIjpudWxsLCJuaWNrTmFtZSI6bnVsbCwiaXAiOm51bGwsImlwVmVyaWZ5IjpudWxsLCJleHAiOjE2Mzc1NzY4MDMsInRva2VuIjpudWxsfQ".getBytes(StandardCharsets.UTF_8));
        System.out.println(new String(decode));
        byte[] decode2 = Base64.getDecoder().decode("eyJoZWFkSW1nIjpudWxsLCJuaWNrTmFtZSI6bnVsbCwiaXAiOm51bGwsImlwVmVyaWZ5IjpudWxsLCJleHAiOjE2Mzc1NzY4MDUsInRva2VuIjpudWxsfQ".getBytes(StandardCharsets.UTF_8));
        System.out.println(new String(decode2));
    }

    // JWT使用的密钥,自定义, 不可泄漏, 作为jwt数字验证经MD5加的盐
    public static final String sercetKey = "BSftz3q8uJqgpf";

    // Token过期时间: 1800000毫秒 : 30分钟  1年 : 365*24*60*60*1000 毫秒
    public final static long keeptime = 365*24*60*60*1000;

    // 存入payLoad的参数名
    public final static String PAYLOAD_USER_ID = "userId";                   // 用户唯一标识token, 如需jwtToken过期, 更改token即可
    public final static String PAYLOAD_USER_WX_HEAD_IMG = "userWxHeadImg";           // 微信头像地址
    public final static String PAYLOAD_USER_WX_NICK_NAME = "userWxName";         // 微信昵称 (需要URLEncoder转码)
    public final static String PAYLOAD_USER_IP_ADDRESS = "userAddrIp";                 // ip地址
    public final static String WHETHER_OPEN_ADDRESS_VERIFICATION = "ipVerify"; // 是否开启ip地址验证 true:开启 false:不开启

    /**
     * 获取一个JWTToken
     * @param payLoadMap : 存入payLoad的数据
     * @return
     */
    public static String generToken(Map<String, Object> payLoadMap) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        // 使用Hash256算法进行加密
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        // 获取系统时间以便设置token有效时间
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(sercetKey);
        // 将密钥转码为base64形式,再转为字节码
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
        // 对其使用Hash256进行加密
        JwtBuilder builder = Jwts.builder().setIssuedAt(now);
        // JWT生成类,此时设置iat,以及根据传入的id设置token
        if (payLoadMap != null) {
            builder.setClaims(payLoadMap);
        }
        builder.signWith(signatureAlgorithm, signingKey);
        // 进行签名,生成Signature
        long expMillis = nowMillis + keeptime;
        builder.setExpiration(new Date(expMillis));
        // 返回最终的token结果
        return builder.compact();
    }


    /**
     * 验证并更新jwtToken
     * @param jwtToken : 需要验证的jwtToken
     * @return
     */
    public static String updateToken(String jwtToken) {
        Map<String, Object> payLoadMap = new HashMap<>();
        // Claims就是包含了我们的Payload信息类
        Claims claims = verifyToken(jwtToken);
        // 获取payLoad中的uuid, ip, verify
        Object id = claims.get(PAYLOAD_USER_ID);
        Object headImg = claims.get(PAYLOAD_USER_WX_HEAD_IMG);
        Object nickName = claims.get(PAYLOAD_USER_WX_NICK_NAME);
        Object ip = claims.get(PAYLOAD_USER_IP_ADDRESS);
        Object verify = claims.get(WHETHER_OPEN_ADDRESS_VERIFICATION);
        // 转存map
        payLoadMap.put(PAYLOAD_USER_ID, id);
        payLoadMap.put(PAYLOAD_USER_WX_HEAD_IMG, headImg);
        payLoadMap.put(PAYLOAD_USER_WX_NICK_NAME, nickName);
        payLoadMap.put(PAYLOAD_USER_IP_ADDRESS, ip);
        payLoadMap.put(WHETHER_OPEN_ADDRESS_VERIFICATION, verify);
        // 重新获取Token并返回
        return generToken(payLoadMap);
    }


    /**
     * 解析用户id
     * @return
     */
    public static Integer parseUserId(HttpServletRequest request){
        String jwtToken = null;
        String payLoad = null;
        Cookie[] cookies = request.getCookies();
        if(cookies!=null && cookies.length!=0){
            for (Cookie cookie : cookies) {
                if("jwtToken".equals(cookie.getName())){
                    jwtToken = cookie.getValue();
                }
            }
        }else{
            jwtToken = request.getAttribute("cookie").toString();
        }
        if(jwtToken != null){
            payLoad = new String(Base64.getDecoder().decode(jwtToken.split("\\.")[1]), StandardCharsets.UTF_8);
            String userId = payLoad.split(",")[5].split(":")[1].replaceAll("}", "");
            return Integer.parseInt(userId);
        }
        return null;
    }

    /**
     * 验证token
     * @param token : 需要验证的token
     * @return  : 有效则返回Claims, 包含了payLoad信息, 无效则抛出异常
     */
    public static Claims verifyToken(String token) {
        Claims claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(sercetKey))
                .parseClaimsJws(token).getBody();
        return claims;
    }
}
